//
// Created by denglibin on 19-10-24.
//
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

static const int port = 8888;


static void start_server(){
    int sock_fd;//socket文件描述符
    struct sockaddr_in my_sock;//地址
    struct sockaddr_in client_addr;//客户端的ip和端口
    int new_fd; //代表客户端socket的文件文件描述符
    char buf[1024] = {0}, client_ip_buf[128] = {0}, send_buf[1024] = {0};//缓冲区
    //初始化socket
    //socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字，而socket()用于创建一个socket描述符（socket descriptor），它唯一标识一个socket。
    //这个socket描述字跟文件描述字一样，后续的操作都有用到它，把它作为参数，通过它来进行一些读写操作。正如可以给fopen的传入不同参数值，以打开不同的文件。
    //创建socket的时候，也可以指定不同的参数创建不同的socket描述符，socket函数的三个参数分别为：
    //
    //domain：即协议域，又称为协议族（family）。常用的协议族有，AF_INET、AF_INET6、AF_LOCAL（或称AF_UNIX，Unix域socket）、AF_ROUTE等等。
    //协议族决定了socket的地址类型，在通信中必须采用对应的地址，如AF_INET决定了要用ipv4地址（32位的）与端口号（16位的）的组合、AF_UNIX决定了要用一个绝对路径名作为地址。对于TCP/IP协议族，该参数置AF_INET。
    //
    //type：指定socket类型。常用的socket类型有，流套接字类型SOCK_STREAM、数据报套接字类型SOCK_DGRAM、原始套接字SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
    //
    //protocol：就是指定协议。常用的协议有，IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等，它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
    //
    //注意：并不是上面的type和protocol可以随意组合的，如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时，会自动选择type类型对应的默认协议。
    //
    //当我们调用socket创建一个socket时，返回的socket描述字它存在于协议族（address family，AF_XXX）空间中，但没有一个具体的地址。
    //如果想要给它赋值一个地址，就必须调用bind()函数，否则就当调用connect()、listen()时系统会自动随机分配一个端口
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    //编辑地址信息
    //memset(&my_sock, 0, sizeof(my_sock));
    bzero(&my_sock, sizeof(my_sock));
    my_sock.sin_family = AF_INET; //协议域
    my_sock.sin_port = htons(port);//端口 htons是将整型变量从主机字节顺序转变成网络字节顺序， 就是整数在地址空间存储方式变为高位字节存放在内存的低地址处(大端序) s表示short 16位
    my_sock.sin_addr.s_addr = htonl(INADDR_ANY);
    //绑定地址 然后监听
    int ret = bind(sock_fd, (struct sockaddr *)&my_sock, sizeof(struct sockaddr));
    if(ret != 0){
        perror("bind:");
        exit(0);
    }
    // 参数1：套接字文件描述符， 侦听队列长度(接受的连接数)
    if(listen(sock_fd, 8) < 0){
        printf("listen error!\n");
        exit(-1);
    }
    socklen_t sin_size = sizeof(struct sockaddr_in);
    printf("listening...\n");
    // 阻塞 等待客户端的连接
    new_fd = accept(sock_fd, (struct sockaddr*)&client_addr, &sin_size);
    if(new_fd < 0){
        perror("accept:");
        exit(-1);
    }
    inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_ip_buf, sizeof(client_ip_buf));
    printf("client ip = %s, port = %d\n", client_ip_buf, ntohs(client_addr.sin_port));
    while(1){
        int len = recv(new_fd, buf, sizeof(buf), 0);
        printf("%d->%s", len, buf);
        if(len == 0){
            printf("client disconnected\n");
            close(new_fd);
            break;
        }
        fgets(send_buf, sizeof(send_buf), stdin); //获取输入
        if(strlen(send_buf) > 0){
            send(new_fd, send_buf, strlen(send_buf), 0);
        }
        memset(buf, 0, sizeof(buf));
        memset(send_buf, 0, sizeof(send_buf));
    }
    close(sock_fd);
}

static int main_(void){
    start_server();
    return 0;
}
